// random.cpp

#include "CvGameCoreDLL.h"
#include "CvRandom.h"
#include "CvGlobals.h"
#include "CyArgsList.h"
#include "CvGameAI.h"
// Start GoodRandom added by torin 2007 Feb 5
#include "mtprng.h"
// End GoodRandom

#define RANDOM_A      (1103515245)
#define RANDOM_C      (12345)
#define RANDOM_SHIFT  (16)

CvRandom::CvRandom()
{
	reset();
    // Start GoodRandom added by torin 2007 Feb 5
	m_mtprng = new libcoyote::mtprng(MTPRNG_SEED);
    // End GoodRandom
}


CvRandom::~CvRandom()
{
	uninit();
}


void CvRandom::init(unsigned long ulSeed)
{
	//--------------------------------
	// Init saved data
	reset(ulSeed);
    // Start GoodRandom added by torin 2007 Feb 5
    m_mtprng->init(ulSeed);
    // End GoodRandom

	//--------------------------------
	// Init non-saved data
}


void CvRandom::uninit()
{
}


// FUNCTION: reset()
// Initializes data members that are serialized.
void CvRandom::reset(unsigned long ulSeed)
{
	//--------------------------------
	// Uninit class
	uninit();

	m_ulRandomSeed = ulSeed;
}


unsigned short CvRandom::get(unsigned short usNum, const TCHAR* pszLog)
{
    // Start GoodRandom added by torin 2007 Feb 5
    // use the MT to yield a short in the range [0, usNum)
    // make sure to return 0 if we were given 0
    unsigned short usNext = (unsigned short) (usNum ? m_mtprng->get_rand_range(0, usNum - 1) : 0);
    // End GoodRandom

	if (pszLog != NULL)
	{
		if (GC.getLogging() && GC.getRandLogging())
		{
			if (GC.getGameINLINE().getTurnSlice() > 0)
			{
				TCHAR szOut[1024];
                // Start GoodRandom added by torin 2007 Feb 5
                // Change the debug message to actually be useful
				sprintf(szOut, "RandShort = %u of %u on turn %u (%s)\n", usNext, usNum, GC.getGameINLINE().getTurnSlice(), pszLog);
                // End GoodRandom
				gDLL->messageControlLog(szOut);
			}
		}
	}

// Start GoodRandom added by torin 2007 Feb 5
//	m_ulRandomSeed = ((RANDOM_A * m_ulRandomSeed) + RANDOM_C);

//	unsigned short usNext = ((unsigned short)((((m_ulRandomSeed >> RANDOM_SHIFT) & MAX_UNSIGNED_SHORT) * ((unsigned long)usNum)) / (MAX_UNSIGNED_SHORT + 1)));
// End GoodRandom

    return usNext;
}


// Start GoodRandom added by torin 2007 Feb 5
// sometimes CvRandom::get is called with values outside of the range of an unsigned short, e.g. getCommerceBase
// provide an integer random that will yield the full range
unsigned int CvRandom::getInt(unsigned int uiNum, const TCHAR* pszLog)
{
    // use the MT to yield a short in the range [0, usNum)
    // make sure to return 0 if we were given 0
    unsigned int uiNext = (uiNum ? m_mtprng->get_rand_range(0, uiNum - 1) : 0);

	if (pszLog != NULL)
	{
		if (GC.getLogging() && GC.getRandLogging())
		{
			if (GC.getGameINLINE().getTurnSlice() > 0)
			{
				TCHAR szOut[1024];
				sprintf(szOut, "RandInt = %u of %u on %u (%s)\n", uiNext, uiNum, GC.getGameINLINE().getTurnSlice(), pszLog);
				gDLL->messageControlLog(szOut);
			}
		}
	}

    return uiNext;
}
// End GoodRandom


float CvRandom::getFloat()
{
// Start GoodRandom added by torin 2007 Feb 5
//	return (((float)(get(MAX_UNSIGNED_SHORT))) / ((float)MAX_UNSIGNED_SHORT));
    return (float) m_mtprng->get_rand_real2();
// End GoodRandom
}


void CvRandom::reseed(unsigned long ulNewValue)
{
	m_ulRandomSeed = ulNewValue;
    m_mtprng->init(ulNewValue);
}


unsigned long CvRandom::getSeed()
{
	return m_ulRandomSeed;
}


void CvRandom::read(FDataStreamBase* pStream)
{
	reset();

	pStream->Read(&m_ulRandomSeed);
}


void CvRandom::write(FDataStreamBase* pStream)
{
    //gDLL->logMsg("MAF.log", "CvRandom::write entered");
	pStream->Write(m_ulRandomSeed);
	//gDLL->logMsg("MAF.log", "CvRandom::write exit");
}
